<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第14章 - ジェネレーター</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="13-I18n-and-L10n.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="15-Unit-and-Functional-Testing.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.14.admin.generator" id="chapter.14.admin.generator"></a><h1>第14章 - ジェネレーター</h1>

<blockquote class="caution"><p>
  この章では1.0のフォームシステムをまだ利用しているadminジェネレーターを説明しています。CRUDジェネレーターに関する情報は新しいフォームシステムを利用する"<a href="http://www.symfony-project.org/book/forms/1_1/ja/04-Propel-Integration">symfony Forms in Action</a>" bookに移動しました。</p>
</blockquote>

<p>多くのアプリケーションはデータベースに保存されたデータに基づいており、それにアクセスするためのインターフェイスを提供します。symfonyはPropelのオブジェクトに基づいたデータ操作機能を提供するモジュール作成の反復的なタスクを自動化します。オブジェクトモデルが適切に定義したのであれば、symfonyはサイト全体の管理機能(administration)も自動的に生成します。この章では、symfonyに搭載された2つのジェネレーター: scaffoldingジェネレーターとadministrationジェネレーターをお教えします。後者は完全な構文による特別な設定ファイルに依存するので、この章の多くはadministrationジェネレーターのさまざまな可能性について説明します。</p>

<div class="toc">
<dl>
<dt><a href="#code.generation.based.on.the.model">14.1. モデルに基づいたコード生成</a></dt>
<dd><dl>
<dt><a href="#example.data.model">14.1.1. データモデルの例</a></dt>
</dl></dd>
<dt><a href="#administration">14.2. administration</a></dt>
<dd><dl>
<dt><a href="#initiating.an.administration.module">14.2.1. administrationモジュールを初期化する</a></dt>
<dt><a href="#a.look.at.the.generated.code">14.2.2. 生成されたコードを見る</a></dt>
<dt><a href="#introducing.the.generator.yml.configuration.file">14.2.3. generator.yml設定ファイルを導入する</a></dt>
</dl></dd>
<dt><a href="#generator.configuration">14.3. ジェネレーターの設定</a></dt>
<dd><dl>
<dt><a href="#fields">14.3.1. フィールド</a></dt>
<dd><dl>
<dt><a href="#field.settings">14.3.1.1. フィールドの設定</a></dt>
<dt><a href="#adding.fields.to.the.display">14.3.1.2. フィールドをdisplay設定に追加する</a></dt>
<dt><a href="#custom.fields">14.3.1.3. カスタムフィールド</a></dt>
<dt><a href="#partial.fields">14.3.1.4. 部分テンプレートフィールド</a></dt>
</dl></dd>
<dt><a href="#view.customization">14.3.2. ビューのカスタマイゼーション</a></dt>
<dd><dl>
<dt><a href="#changing.the.view.title">14.3.2.1. ビューのタイトルを変更する</a></dt>
<dt><a href="#adding.tooltips">14.3.2.2. ツールチップを追加する</a></dt>
<dt><a href="#modifying.the.date.format">14.3.2.3. 日付の書式を修正する</a></dt>
</dl></dd>
<dt><a href="#list.viewspecific.customization">14.3.3. listビュー固有のカスタマイズ</a></dt>
<dd><dl>
<dt><a href="#changing.the.layout">14.3.3.1. レイアウトを変更する</a></dt>
<dt><a href="#filtering.the.results">14.3.3.2. 結果をフィルタリングする</a></dt>
<dt><a href="#sorting.the.list">14.3.3.3. リストをソートする</a></dt>
<dt><a href="#customizing.the.pagination">14.3.3.4. パジネーションをカスタマイズする</a></dt>
<dt><a href="#using.a.join.to.speed.up.page.delivery">14.3.3.5. ページ配信を加速するためにJoinを使う</a></dt>
</dl></dd>
<dt><a href="#edit.viewspecific.customization">14.3.4. editビュー固有のカスタマイズ</a></dt>
<dd><dl>
<dt><a href="#changing.the.input.type">14.3.4.1. 入力タイプを変更する</a></dt>
<dt><a href="#handling.partial.fields">14.3.4.2. 部分テンプレートフィールドを扱う</a></dt>
</dl></dd>
<dt><a href="#dealing.with.foreign.keys">14.3.5. 外部キーを扱う</a></dt>
<dd><dl>
<dt><a href="#onetomany.relationships">14.3.5.1. 一対多のリレーション</a></dt>
<dt><a href="#manytomany.relationships">14.3.5.2. 多対多のリレーション</a></dt>
</dl></dd>
<dt><a href="#adding.interactions">14.3.6. インタラクションを追加する</a></dt>
<dt><a href="#form.validation">14.3.7. フォームのバリデーション</a></dt>
<dt><a href="#restricting.user.actions.using.credentials">14.3.8. クレデンシャルを利用してユーザーのアクションを制限する</a></dt>
</dl></dd>
<dt><a href="#modifying.the.presentation.of.generated.modules">14.4. 生成されたモジュールのプレゼンテーションを修正する</a></dt>
<dd><dl>
<dt><a href="#using.a.custom.style.sheet">14.4.1. カスタムスタイルシートを使う</a></dt>
<dt><a href="#creating.a.custom.header.and.footer">14.4.2. カスタムヘッダーとフッターを生成する</a></dt>
<dt><a href="#customizing.the.theme">14.4.3. テーマをカスタマイズする</a></dt>
</dl></dd>
<dt><a href="#summary">14.5. まとめ</a></dt>
</dl>
</div>
<a name="code.generation.based.on.the.model" id="code.generation.based.on.the.model"></a><h2>モデルに基づいたコード生成</h2>

<p>Webアプリケーションにおいて、データアクセスのオペレーションはつぎのように分類できます:</p>

<ul>
<li>レコードの作成</li>
<li>レコードの検索</li>
<li>レコードの更新(とカラムの修正)</li>
<li>レコードの削除</li>
</ul>

<p>これらのオペレーションは共通なので、頭文字を取った専用の略語であるCRUD(Create、Retrieval、Update、Deletion)が存在します。多くのページはこれらの1つに還もとできます。たとえば、フォーラムのアプリケーションにおいて、最新投稿のリストは検索オペレーション(retrieve)で、投稿への返答は作成オペレーション(create)に対応します。</p>

<p>任意のテーブルに対するCRUDオペレーションを実装する基本的なアクションとテンプレートはWebアプリケーション内部で繰り返し作られます。symfonyにおいて、バックエンドインターフェイスの初期の部分を加速するために、モデルレイヤーはCRUDオペレーションを生成できるようにするための十分な情報を持ちます。</p>

<a name="example.data.model" id="example.data.model"></a><h3>データモデルの例</h3>

<p>この章全体を通して、リストはシンプルな例に基づいたsymfonyのadminジェネレーターの機能を示します。これによって8章を思い出すでしょう。これは2つの<code>Article</code>クラスと<code>Comment</code>クラスを含む、blogのアプリケーションのよく知られた例です。図14-1で描かれているように、リスト14-1はスキーマを示します。</p>

<p>リスト14-1 - blogのアプリケーションの<code>schema.yml</code>の例</p>

<pre><code>propel:
  blog_article:
    _attributes: { phpName: Article }
    id:
    title:       varchar(255)
    content:     longvarchar
    created_at:
  blog_comment:
    _attributes: { phpName: Comment }
    id:
    article_id:
    author:      varchar(255)
    content:     longvarchar
    created_at:
</code></pre>

<p>図14-1 データモデルの例</p>

<p><img src="images/F1401.png" alt="データモデルの例" title="データモデルの例" /></p>

<p>コード生成を可能にするためにスキーマ作成の期間で従わなければならない特別なルールは存在しません。symfonyはスキーマをそのまま使用し、administrationを生成するためにスキーマの属性を解釈します。</p>

<blockquote class="tip"><p>
  この章を最大限に活用するには、例の内容を実際に行う必要があります。リストで説明されたすべてのステップを眺めるのであれば、symfonyが何を生成し、生成されたコードで何が行われるのかということをより理解できるようになります。以まえに説明されたようなデータ構造を作成したり、<code>blog_article</code>と<code>blog_comment</code>テーブルをともなうデータベースを作成したり、サンプルデータをこのデータベースに投入したくなるでしょう。</p>
</blockquote>

<a name="administration" id="administration"></a><h2>administration</h2>

<p>symfonyは、アプリケーションのバックエンドのために、<code>schema.yml</code>ファイルからのモデルクラス定義に基づいて、モジュールを作成できます。生成されたadministrationモジュールだけを用いてサイト全体のadministrationを作成できます。このセクションの例において<code>backend</code>アプリケーションに追加されたadministraionモジュールを説明します。プロジェクトが<code>backend</code>アプリケーションを持たない場合、<code>generate:app</code>タスクを呼び出してスケルトンを作成してください:</p>

<pre><code>&gt; php symfony generate:app backend
</code></pre>

<p>administrationモジュールは<code>generator.yml</code>という名前の特別な設定ファイルを通してモデルを解釈します。すべての生成されたコンポーネントとモジュールの外見を拡張するために<code>generator.yml</code>ファイルを変更できます。このようなモジュールは通常のモジュールメカニズムからの恩恵を受けます(レイアウト、バリデーション、ルーティング、カスタム設定、オートロードなど)。独自機能を生成されたadministrationに統合するために、生成されたアクションもしくはテンプレートをオーバーライドすることもできますが、<code>generator.yml</code>はもっとも共通の要件を考慮して、PHPのコードの使いかたを限定的なものにします。</p>

<blockquote class="note"><p>
  adminジェネレーターはsymfonyのバリデーションシステムを使うので、<code>sfCompat10</code>プラグインが自動的に有効になります。</p>
</blockquote>

<a name="initiating.an.administration.module" id="initiating.an.administration.module"></a><h3>administrationモジュールを初期化する</h3>

<p>symfonyによって、モジュールを基本単位としてadministrationをビルドします。モジュールは<code>propel:init-admin</code>タスクを利用するPropelオブジェクトに基づいて生成されます:</p>

<pre><code>&gt; php symfony propel:init-admin backend article Article
</code></pre>

<p>この呼び出しだけで<code>backend</code>アプリケーションのなかに<code>Article</code>クラスの定義に基づいた<code>article</code>モジュールが作成されるので、つぎのURLからアクセスできます:</p>

<pre><code>http://localhost/backend.php/article
</code></pre>

<p>図14-5、図14-6で描かれている生成されたモジュールの外見は商業用のアプリケーションとしてそのまま利用できるほど十分に洗練されています。</p>

<p>図14-5 - <code>backend</code>アプリケーション内部の<code>article</code>モジュールの<code>list</code>ビュー</p>

<p><img src="images/F1405.png" alt="backendアプリケーション内部のarticleモジュールのlistビュー" title="backendアプリケーション内部のarticleモジュールのlistビュー" /></p>

<p>図14-6 - バックエンドで<code>article</code>モジュールの<code>edit</code>ビュー</p>

<p><img src="images/F1406.png" alt="バックエンドでarticleモジュールのeditビュー" title="バックエンドでarticleモジュールのeditビュー" /></p>

<a name="a.look.at.the.generated.code" id="a.look.at.the.generated.code"></a><h3>生成されたコードを見る</h3>

<p><code>apps/backend/modules/article/</code>ディレクトリ内のadministrationの<code>Article</code>モジュールのコードは初期化だけ行われたので空です。このモジュールの生成されたコードを吟味するための最良の方法はブラウザーを利用してこれと情報のやりとりをすることと<code>cache/</code>フォルダーの内容を確認することです。リスト14-4はキャッシュで見つかる生成されたアクションとテンプレートのリストを表示します。</p>

<p>リスト14-4 - 生成されたadministrationの要素(<code>cache/backend/ENV/modules/article/</code>)</p>

<pre><code>// actions/actions.class.phpにおいて
create           // editにフォワードする
delete           //レコードを削除する
edit             // レコードのフィールドを修正するためにフォームを表示する
                 // フォーム投稿を扱う
index            // listに進む
list             // テーブルのすべてのレコードのリストを表示する
save             // editに進む

// templates/において
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php
editSuccess.php
listSuccess.php
</code></pre>

<p>これは生成されたadministrationモジュールがおもに2つのビュー、<code>edit</code>ビューと<code>list</code>ビューで構成されることを示します。コードを見てみると、モジュール性が非常に高く、読みやすく拡張性のあるものであることがわかります。</p>

<a name="introducing.the.generator.yml.configuration.file" id="introducing.the.generator.yml.configuration.file"></a><h3>generator.yml設定ファイルを導入する</h3>

<p>生成されたadministrationモジュールはYAML形式の<code>generator.yml</code>設定ファイルで見つかるパラメーターに依存します。新しく生成されたadministrationモジュールのデフォルト設定を見るには、リスト14-5で再現されている、<code>backend/modules/article/config/</code>ディレクトリに設置された<code>generator.yml</code>ファイルを開いてください。</p>

<p>リスト14-5 - ジェネレーターのデフォルト設定(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default
</code></pre>

<p>この設定は基本的なadministrationを生成するために十分なものです。カスタマイズ内容は<code>param</code>キー下に<code>theme</code>の行以降に追加します(<code>generator.yml</code>ファイルの下側に追加されたすべての行は少なくとも適切にインデントされた4つの空白スペースで始めなければならないことを意味します)。リスト14-6はカスタマイズされた典型的な<code>generator.yml</code>を示しています。</p>

<p>リスト14-6 - 典型的なジェネレーターの完全な設定</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      author_id:      { name: Article author }

    list:
      title:          List of all articles
      display:        [title, author_id, category_id]
      fields:
        published_on: { params: date_format='dd/MM/yy' }
      layout:         stacked
      params:         |
        %%is_published%%&lt;strong&gt;%%=title%%&lt;/strong&gt;&lt;br /&gt;&lt;em&gt;by %%author%%
        in %%category%% (%%published_on%%)&lt;/em&gt;&lt;p&gt;%%content_summary%%&lt;/p&gt;
      filters:        [title, category_id, author_id, is_published]
      max_per_page:   2

    edit:
      title:          Editing article "%%title%%"
      display:
        "Post":       [title, category_id, content]
        "Workflow":   [author_id, is_published, created_on]
      fields:
        category_id:  { params: disabled=true }
        is_published: { type: plain}
        created_on:   { type: plain, params: date_format='dd/MM/yy' }
        author_id:    { params: size=5 include_custom=&gt;&gt; Choose an author &lt;&lt; }
        published_on: { credentials:  }
        content:      { params: rich=true tinymce_options=height:150 }
</code></pre>

<p>つぎのセクションでこの設定ファイルで利用可能なすべてのパラメーターの詳細内容を説明します。</p>

<a name="generator.configuration" id="generator.configuration"></a><h2>ジェネレーターの設定</h2>

<p>ジェネレーターの設定ファイルはとても強力で、生成されたadministrationを多くの方法で変更できます。しかしこの機能には代償があります: 全体の構文の記述が読んで学ぶには長いので、文章で説明したら、この章がこの本でもっとも長くなってしまいます。ですのでsymfonyのWebサイトはこの構文を学ぶための助けになる追加リソースを提示します: administrationジェネレーターのチートシートが図14-7で再現されてます。<a href="http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdf">http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdf</a>から保存し、この章のつぎの例を読むときにこれを頭のなかにとどめておいてください。</p>

<p>このセクションの例は、<code>Comment</code>クラス定義に基づいて、administrationの<code>comment</code>モジュールと同様に、administrationの<code>article</code>モジュールを調整します。<code>propel:init-admin</code>タスクで後者を作成してください:</p>

<pre><code>&gt; php symfony propel:init-admin backend comment Comment
</code></pre>

<p>図14-7 - administrationジェネレーターのチートシート</p>

<p><img src="images/F1407.png" alt="administrationジェネレーターチートのシート" title="administrationジェネレーターチートのシート" /></p>

<a name="fields" id="fields"></a><h3>フィールド</h3>

<p>デフォルトでは、<code>list</code>ビューのカラムと<code>edit</code>ビューのフィールドは<code>schema.yml</code>で定義されたカラムです。<code>generator.yml</code>によって、どのフィールドが表示され、隠されるかを選ぶことが可能で、これらがオブジェクトモデルに直接対応していなくても、独自のフィールドを追加できます。</p>

<a name="field.settings" id="field.settings"></a><h4>フィールドの設定</h4>

<p>administrationジェネレーターは<code>schema.yml</code>ファイルのカラムごとに<code>field</code>を作ります。<code>fields</code>キーの下で、それぞれのフィールドの表示方法やフォーマット方法などを修正できます。たとえば、リスト14-7で示されるフィールドの設定は<code>title</code>フィールド用のカスタムラベルクラスと入力タイプ、そして<code>content</code>フィールド用のラベルとツールチップを定義します。つぎのセクションでそれぞれのパラメーターが動作する方法を詳細に説明します。</p>

<p>リスト14-7 - カラムに対してカスタムラベルを設定する</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title, type: textarea_tag, params: class=foo }
      content:        { name: Body, help: Fill in the article body }
</code></pre>

<p>リスト14-8でお手本が示されているように、すべてのビューに対するこのデフォルトの定義に加えて、任意のビュー(<code>list</code>と<code>edit</code>)のためのフィールドの設定をオーバーライドできます。</p>

<p>リスト14-8 - ビュー単位でグローバルな設定ビューをオーバーライドする</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title }
      content:        { name: Body }

    list:
      fields:
        title:        { name: Title }

    edit:
      fields:
        content:      { name: Body of the article }
</code></pre>

<p>一般的な原則があります: <code>fields</code>キーの下のモジュール全体に対して設定される任意の設定も後に続くビュー固有の領域(<code>list</code>と<code>edit</code>)でオーバーライドできます。</p>

<a name="adding.fields.to.the.display" id="adding.fields.to.the.display"></a><h4>フィールドをdisplay設定に追加する</h4>

<p><code>fields</code>セクションで定義したフィールドはそれぞれのビューに対して表示、隠す、順番に並べるなど、さまざまな方法で分類できます。<code>display</code>キーはこの目的のために使われます。たとえば、<code>comment</code>モジュールのフィールドを順番に並べるには、リスト14-9のコードを使います。</p>

<p>リスト14-9 表示フィールドを選択する(<code>modules/comment/config/generator.yml</code>)</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    fields:
      article_id:     { name: Article }
      created_at:     { name: Published on }
      content:        { name: Body }

    list:
      display:        [id, article_id, content]

    edit:
      display:
        NONE:         [article_id]
        Editable:     [author, content, created_at]
</code></pre>

<p>図14-8のように<code>list</code>は3つのカラムを表示し、図14-9のように、<code>edit</code>フォームは2つのグループに集められた4つのフィールドを表示します。</p>

<p>図14-8 - <code>comment</code>モジュールの<code>list</code>ビュー内部のカスタムカラム設定</p>

<p><img src="images/F1408.png" alt="commentモジュールのlistビュー内部のカスタムカラム設定" title="commentモジュールのlistビュー内部のカスタムカラム設定" /></p>

<p>図14-9 - <code>comment</code>モジュールの<code>edit</code>ビュー内部でフィールドを分類する</p>

<p><img src="images/F1409.png" alt="commentモジュールのeditビュー内でフィールドを分類する" title="commentモジュールのeditビュー内でフィールドを分類する" /></p>

<p><code>display</code>設定を2つの方法で利用できます:</p>

<ul>
<li>表示するカラムとそれらが表示される順番を選ぶために、以前の<code>list</code>ビューのように、フィールドをシンプルな配列に記入します。</li>
<li>フィールドを分類するために、グループ名をキーとして持つ連想配列、もしくは名なしのグループに対しては<code>NONE</code>を使います。値は順番に並べられたカラム名の配列です。</li>
</ul>

<blockquote class="tip"><p>
  デフォルトでは、主キーのカラムはどちらのビューでも決して現れません。</p>
</blockquote>

<a name="custom.fields" id="custom.fields"></a><h4>カスタムフィールド</h4>

<p>当然のことながら、<code>generator.yml</code>で設定されたフィールドはスキーマで定義された実際のカラムに対応している必要はありません。関連するクラスがカスタムゲッターを提供する場合、これを<code>list</code>ビューのためのフィールドとして使うことができます; ゲッターかつ/もしくはセッターが存在する場合、このクラスは<code>edit</code>ビューでも利用できます。たとえば、リスト14-10のように、Articleモデルを<code>getNbComments()</code>メソッドで拡張できます。</p>

<p>リスト14-10 - カスタムゲッターをモデルに追加する(<code>lib/model/Article.class.php</code>)</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> getNbComments<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">countComments</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>リスト14-11のように、<code>nb_comments</code>は生成モジュールのなかのフィールドとして利用できます(ゲッターはcamelCaseバージョンのフィールド名を使います)。</p>

<p>リスト14-11 - カスタムゲッターはadministrationモジュールに対して追加カラムを提供する(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    list:
      display:        [id, title, nb_comments, created_at]
</code></pre>

<p><code>article</code>モジュールの<code>list</code>ビューの結果は図14-10で示されます。</p>

<p>図14-10 - <code>article</code>モジュールの<code>list</code>ビュー内のカスタムフィールド</p>

<p><img src="images/F1410.png" alt="articleモジュールのlistビュー内部のカスタムフィールド" title="articleモジュールのlistビュー内部のカスタムフィールド" /></p>

<p>カスタムフィールドは生のデータ以上のものを表示するためにHTMLのコードも返すことができます。たとえば、リスト14-12で示されるような<code>Comment</code>クラスを<code>getArticleLink()</code>メソッドで拡張できます。</p>

<p>リスト14-12 - HTMLを返すカスタムゲッターを追加する(<code>lib/model/Comment.class.php</code>)</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> getArticleLink<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> link_to<span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getArticle</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getTitle</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'article/edit?id='</span><span class="sy0">.</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getArticleId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>リスト14-11と同じ構文でこの新しいゲッターを<code>comment/list</code>ビュー内のカスタムフィールドとして使うことができます。リスト14-13の例と、図14-11の結果をご覧ください。ゲッター(記事のハイパーリンク)によって出力されるHTMLのコードは記事の主キーの代わりに2番目のカラムに現れます。</p>

<p>リスト14-13 - HTML結果を返すカスタムゲッターは追加カラムとしても使える(<code>modules/comment/config/generator.yml</code>)</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    list:
      display:        [id, article_link, content]
</code></pre>

<p>図14-11 - <code>comment</code>モジュールの<code>list</code>ビュー内部のカスタムフィールド</p>

<p><img src="images/F1411.png" alt="commentモジュールのlistビュー内部のカスタムフィールド" title="commentモジュールのlistビュー内部のカスタムフィールド" /></p>

<a name="partial.fields" id="partial.fields"></a><h4>部分テンプレートフィールド</h4>

<p>モデルに設置されたコードはプレゼンテーションから独立していなければなりません。前出の<code>getArticleLink()</code>メソッドの例はレイヤー分離の原則を順守していません。ビューのコードのなかにはモデルレイヤーに現れるものがあるからです。正しい方法で、同じ結果を実現するには、カスタムフィールドに対応したHTMLを出力するコードは部分テンプレートで対応するほうがベターです。幸いにして、administrationジェネレーターによってアンダースコアのプレフィックスを持つフィールド名を宣言できます。この場合、リスト14-13の<code>generator.yml</code>ファイルはリスト14-14のように修正されます。</p>

<p>リスト14-14 - <code>_</code>のプレフィックスをつけることで部分テンプレートを追加カラムとして使うことができる</p>

<pre><code>    list:
      display:        [id, _article_link, created_at]
</code></pre>

<p>これを機能させるには、リスト14-15で示されるように、<code>_article_link.php</code>部分テンプレートを<code>modules/comment/tempaltes/</code>ディレクトリのなかに作らなければなりません。</p>

<p>リスト14-15 - <code>list</code>ビューのための部分テンプレートの例(<code>modules/comment/templates/_article_link.php</code>)</p>

<pre><code>&lt;?php echo link_to($comment-&gt;getArticle()-&gt;getTitle(), 'article/edit?id='.$comment-&gt;getArticleId()) ?&gt;
</code></pre>

<p>部分テンプレートフィールドの部分テンプレートテンプレートはクラスによって命名された変数(この例では<code>$comment</code>)を通して現在のオブジェクトにアクセスできることに注意してください。たとえば、<code>UserGroup</code>という名前のクラスのためにビルドされたモジュールに対して、部分テンプレートは<code>$user_group</code>変数を通して現在のオブジェクトにアクセスできるようになります。</p>

<p>レイヤー分離の原則が順守されることを除いて、結果は図14-11と同じです。レイヤー分離の原則を順守することに慣れたら、アプリケーションはより維持しやすくなります。</p>

<p>部分テンプレートフィールドのパラメーターをカスタマイズする必要がある場合、<code>field</code>キーの下で通常のフィールドと同じ事を行います。アンダースコア(<code>_</code>)をキーの先頭に含めないでください。リスト14-16をご覧ください。</p>

<p>リスト14-16 - 部分テンプレートフィールドのプロパティは<code>fields</code>キーの下でカスタマイズできる</p>

<pre><code>      fields:
        article_link:   { name: Article }
</code></pre>

<p>部分テンプレートがロジックで混雑するようになったら、おそらくはこれをコンポーネントで置き換えたいと思うでしょう。リスト14-17のように、<code>_</code>のプレフィックスを<code>~</code>に変更することで、コンポーネントフィールドを定義できます。</p>

<p>リスト14-17 - コンポーネントは追加のカラムとして利用できる。<code>~</code>のプレフィックスを使う</p>

<pre><code>    ...
    list:
      display:        [id, ~article_link, created_at]
</code></pre>

<p>生成されたテンプレートにおいて、現在のモジュールの<code>articleLink</code>コンポーネントを呼び出すことでこれは生成されます。</p>

<blockquote class="note"><p>
  カスタムと部分テンプレートのフィールドは<code>list</code>ビュー、<code>edit</code>ビューのなかと、フィルターに対して利用できます。いくつかのビューに対して同じ部分テンプレートを使う場合、コンテキスト(<code>'list'</code>、<code>'edit'</code>、もしくは <code>'filter'</code>)が<code>$type</code>変数に保存されます。</p>
</blockquote>

<a name="view.customization" id="view.customization"></a><h3>ビューのカスタマイゼーション</h3>

<p><code>edit</code>ビューと<code>list</code>ビューの外見を変更するために、テンプレートを変えたいと思うことがあります。しかしこれらは自動的に生成されるので、これを行うのはあまりよいアイディアではありません。代わりに<code>generator.yml</code>設定ファイルを使用すべきです。モジュール性を損なうことなくほとんどすべての必要なことを行うことができるからです。</p>

<a name="changing.the.view.title" id="changing.the.view.title"></a><h4>ビューのタイトルを変更する</h4>

<p>フィールドのカスタムセットに加えて、<code>list</code>ページと<code>edit</code>ページはカスタムページタイトルを持ちます。たとえば、<code>article</code>ビューのタイトルをカスタマイズしたい場合、リスト14-18のように行います。<code>edit</code>ビューの結果は図14-12に描かれています</p>

<p>リスト14-18 - それぞれのビューに対してカスタムタイトルを設定する(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre><code>    list:
      title:          List of Articles
      ...

    edit:
      title:          Body of article %%title%%
      display:        [content]
</code></pre>

<p>図14-12 - <code>article</code>モジュールの<code>edit</code>ビュー内部のカスタムタイトル</p>

<p><img src="images/F1412.png" alt="articleモジュールのeditビュー内部のカスタムタイトル" title="articleモジュールのeditビュー内部のカスタムタイトル" /></p>

<p>デフォルトのタイトルはクラスの名前を使うので、モデルが明白なクラスの名前を使うのであれば、これらのクラス名で十分であることはよくあります。</p>

<blockquote class="tip"><p>
  <code>generator.yml</code>の文字列の値において、フィールドの値は<code>%%</code>で囲まれたフィールドの名前を通してアクセスできます。</p>
</blockquote>

<a name="adding.tooltips" id="adding.tooltips"></a><h4>ツールチップを追加する</h4>

<p><code>list</code>と<code>edit</code>ビュー内部において、表示されるフィールドを記述するための助けになるツールチップを追加できます。たとえば、ツールチップを<code>comment</code>モジュールの<code>edit</code>ビューの<code>article_id</code>フィールドに追加するには、リスト14-19のように、<code>help</code>プロパティを<code>fields</code>定義のなかに追加します。結果は図14-13に示されています。</p>

<p>リスト14-19 - <code>edit</code>ビューでツールチップを設定する(<code>modules/comment/config/generator.yml</code>)</p>

<pre><code>    edit:
      fields:
        ...
        article_id:   { help: The current comment relates to this article }
</code></pre>

<p>図14-13 - <code>comment</code>モジュールの<code>edit</code>ビュー内部のツールチップ</p>

<p><img src="images/F1413.png" alt="commentモジュールのeditビュー内部のツールチップ" title="commentモジュールのeditビュー内部のツールチップ" /></p>

<p><code>list</code>ビューにおいて、ツールチップはカラムヘッダーに表示されます; <code>edit</code>ビュー内において、これらはinputの下に現れます。</p>

<a name="modifying.the.date.format" id="modifying.the.date.format"></a><h4>日付の書式を修正する</h4>

<p>リスト14-20でお手本が示されているように、<code>date_format</code>パラメーターを使うことで同時にカスタム書式で日付を表示できます。</p>

<p>リスト14-20 <code>list</code>ビューのなかで日付の書式設定をする</p>

<pre><code>    list:
      fields:
        created_at:         { name: Published, params: date_format='dd/MM' }
</code></pre>

<p>このパラメーターは以前の章で説明された<code>format_date()</code>ヘルパーと同じフォーマットパラメーターをとります。</p>

<blockquote class="sidebar"><p class="title">
  administrationテンプレートは国際化の準備ができています</p>
  
  <p>生成されたテンプレートのなかで見つかるすべてのテキストは自動的に国際化されます(たとえば、<code>__()</code>ヘルパーへの呼び出しに囲まれます)。このことは、以前の章で説明されたように、<code>apps/frontend/i18n/</code>ディレクトリ内において、語句の翻訳をXLIFFファイルに追加することで、生成されたadministrationを簡単に翻訳できることを意味します。</p>
</blockquote>

<a name="list.viewspecific.customization" id="list.viewspecific.customization"></a><h3>listビュー固有のカスタマイズ</h3>

<p><code>list</code>ビューは、表形式、もしくはすべての詳細な内容が一行に積み重ねられた状態で、レコードの詳細を表示できます。これはフィルター、パジネーションとソート機能も含みます。これらの機能は設定によって変更できます。詳細はつぎのセクションで説明します。</p>

<a name="changing.the.layout" id="changing.the.layout"></a><h4>レイアウトを変更する</h4>

<p>デフォルトでは、<code>list</code>ビューと<code>edit</code>ビュー間のハイパーリンクは主キーのカラムによって生じています。図14-11に戻ると、コメントリストの<code>id</code>カラムがそれぞれのコメントの主キーを表示するだけでなく、ユーザーが<code>edit</code>ビューにアクセスすることを許可するハイパーリンクを提供します。</p>

<p>別のカラム上に現れるレコードの詳細へのハイパーリンクが望ましいのであれば、<code>display</code>キーの等号(<code>=</code>)をプレフィックスとしてカラムの名前に追加します。リスト14-21は<code>list</code>コメントの表示されるフィールドから<code>id</code>を除去して代わりに<code>content</code>フィールド上にハイパーリンクを置く方法を示しています。図14-14のスクリーンショットを確認してください。</p>

<p>リスト14-21 - <code>edit</code>ビューのためのハイパーリンクを<code>list</code>ビューに移動させる(<code>modules/comment/config/gererator.yml</code>)</p>

<pre><code>    list:
      display:    [article_link, =content]
</code></pre>

<p>図14-14 - <code>comment</code>モジュールの<code>list</code>ビューのなかで、リンクを別のカラム上の<code>edit</code>ビューに移動させる</p>

<p><img src="images/F1414.png" alt="commentモジュールのlistビュー内で、リンクを別のカラム上のeditビューに移動させる" title="commentモジュールのlistビュー内で、リンクを別のカラム上のeditビューに移動させる" /></p>

<p>デフォルトでは、以前示されたように、<code>list</code>ビューは、フィールドがカラムとして表示される<code>tabular</code>レイアウトを使います。しかし<code>stacked</code>レイアウトを使用してフィールドをテーブルの全長を詳しく記述する単独の文字列に連結することもできます。<code>stacked</code>レイアウトを選ぶ場合、<code>params</code>キーにリストのそれぞれの行の値を定義するパターンを組み込まなければなりません。たとえば、リスト14-22は<code>comment</code>モジュールの<code>list</code>ビューに対して<code>stacked</code>レイヤーを定義します。結果は図14-15です。</p>

<p>リスト14-22 - <code>list</code>ビューで<code>stacked</code>レイアウトを使う(<code>modules/comment/cofig/generator.yml</code>)</p>

<pre><code>    list:
      layout:  stacked
      params:  |
        %%=content%% &lt;br /&gt;
        (sent by %%author%% on %%created_at%% about %%article_link%%)
      display:  [created_at, author, content]
</code></pre>

<p>図14-15 - <code>comment</code>モジュールの<code>list</code>ビュー内部のstackedレイアウト</p>

<p><img src="images/F1415.png" alt="commentモジュールのlistビュー内のstackedレイアウト" title="commentモジュールのlistビュー内のstackedレイアウト" /></p>

<p><code>tabular</code>レイアウトは<code>display</code>キーの下でフィールドの配列を必要としますが、<code>stacked</code>レイアウトはそれぞれのレコードに対して生成されたHTMLコードのために<code>params</code>キーを使うことに留意してください。しかしながら、インタラクティブなソートに対して利用できるカラムヘッダーを決定するために<code>display</code>配列が<code>stacked</code>レイアウトでも使われます</p>

<a name="filtering.the.results" id="filtering.the.results"></a><h4>結果をフィルタリングする</h4>

<p><code>list</code>ビューにおいて、フィルターのインタラクションのセットを追加できます。これらのフィルターによって、ユーザーは結果をより少なく表示して速く望むものに到達できます。フィールド名の配列で、<code>filters</code>キーの下にフィルターを設定します。たとえば、図14-16のフィルターボックスと同じようなものを表示するには、リスト14-23のように、<code>article_id</code>、<code>author</code>フィールド、と<code>created_at</code>フィールド上のフィルターをコメントの<code>list</code>ビューに追加します。これを機能させるには、<code>__toString()</code>メソッドを<code>Article</code>クラス(たとえば、記事の<code>title</code>を返す)に追加する必要があります。</p>

<p>リスト14-23 - <code>list</code>ビューでフィルターを設定する(<code>modules/comment/config/generator.yml</code>)</p>

<pre><code>    list:
      filters: [article_id, author, created_at]
      layout:  stacked
      params:  |
        %%=content%% &lt;br /&gt;
        (sent by %%author%% on %%created_at%% about %%article_link%%)
      display:  [created_at, author, content]
</code></pre>

<p>図14-16 - <code>comment</code>モジュールの<code>list</code>ビュー内部のフィルター</p>

<p><img src="images/F1416.png" alt="commentモジュールのlistビュー内部のフィルター" title="commentモジュールのlistビュー内部のフィルター" /></p>

<p>symfonyによって表示されるフィルターはカラム型に依存します:</p>

<ul>
<li>テキストカラム(たとえば<code>comment</code>モジュール内の<code>author</code>フィールド)に対して、フィルターはワイルドカード(<code>*</code>)によるテキストベースの検索を可能にするテキスト入力です。</li>
<li>外部キー(たとえば<code>comment</code>モジュール内部の<code>article_id</code>フィールドのリスト)に対して、フィルターは関連するテーブルのレコードのドロップダウンリストです。通常の<code>object_select_tag()</code>に関しては、ドロップダウンリストのオプションは関連するクラスの<code>__toString()</code>メソッドによって返されるものです。</li>
<li>日付のカラム(たとえば<code>comment</code>カラム内の<code>created_at</code>フィールド)に対して、フィルターはリッチな日付タグのペアで(カレンダーウィジェットによって入力されたテキストフィールド)、時間の間隔を選択できるようにします。</li>
<li>ブール型のカラムに対して、フィルターは<code>true</code>と<code>false</code>と<code>true or false</code>オプションを持つドロップダウンリストです。最後の値はフィルターを再初期化します。</li>
</ul>

<p>リストのなかで部分テンプレートフィールドを使うことと同じように、symfonyが独自に処理できないフィルターを作成するために部分テンプレートフィルターも利用できます。たとえば、2つの値(<code>open</code>と<code>closed</code>)だけ含む<code>state</code>フィールドが存在するが、何らかの理由で、テーブルのリレーションを使う代わりにこれらの値をフィールドに直接保存する理由がある場合を想像してください。このフィールドのシンプルなフィールド(の<code>string</code>型)はテキストベースの検索ですが、あなたが欲しいと思うものはおそらく値のドロップダウンリストです。これを部分テンプレートフィルターで実現することは簡単です。リスト14-24の実装例をご覧ください。</p>

<p>リスト14-24 - 部分テンプレートフィルターを使う</p>

<pre class="php">// templates/_state.phpにおいて、部分テンプレートを定義する
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> select_tag<span class="br0">&#40;</span><span class="st_h">'filters[state]'</span><span class="sy0">,</span> options_for_select<span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span>
  <span class="st_h">''</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span>
  <span class="st_h">'open'</span> <span class="sy0">=&gt;</span> <span class="st_h">'open'</span><span class="sy0">,</span>
  <span class="st_h">'closed'</span> <span class="sy0">=&gt;</span> <span class="st_h">'closed'</span><span class="sy0">,</span>
<span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw3">isset</span><span class="br0">&#40;</span><span class="re0">$filters</span><span class="br0">&#91;</span><span class="st_h">'state'</span><span class="br0">&#93;</span><span class="br0">&#41;</span> ? <span class="re0">$filters</span><span class="br0">&#91;</span><span class="st_h">'state'</span><span class="br0">&#93;</span> <span class="sy0">:</span> <span class="st_h">''</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
// config/generator.ymlにおいて部分テンプレートフィルターをフィルターリストに追加する
    list:
      filters:        [date, _state]</pre>

<p>部分テンプレートが<code>$filters</code>の値にアクセス可能で、フィルターの現在の値を取得するために便利であることを覚えておいてください。</p>

<p>空の値を探すためにとても便利な最後の1つのオプションがあります。著者を持たないコメントを表示するコメントのリストをフィルタリングしたい場合を想像してください。問題はauthorのフィルターを空のままにしておく場合、これが無視されることです。解決方法はリスト14-25のように、<code>filter_is_empty</code>フィールド設定を<code>true</code>に設定と、フィルターは追加のチェックボックスを表示するので、図14-17で図示されているように、空の値を探すことができます。</p>

<p>リスト14-25 - <code>list</code>ビュー内部の<code>author</code>フィールドの上に空の値のフィルタリングを追加する</p>

<pre><code>    list:
      fields:
        author:   { filter_is_empty: true }
      filters:    [article_id, author, created_at]
</code></pre>

<p>図14-17 - 空の<code>author</code>の値をフィルタリングすることを可能にする</p>

<p><img src="images/F1417.png" alt="空のauthorの値をフィルタリングすることを可能にする" title="空のauthorの値をフィルタリングすることを可能にする" /></p>

<a name="sorting.the.list" id="sorting.the.list"></a><h4>リストをソートする</h4>

<p><code>list</code>ビューにおいて、図14-18で示されるように、テーブルのヘッダーはリストを再び順番に並べるために使われるハイパーリンクです。これらのヘッダーは<code>tabular</code>レイアウトと<code>stacked</code>レイアウトの両方で表示されます。これらのリンクをクリックするとリストの順番を再配置する<code>sort</code>パラメーターでページがリロードされます。</p>

<p>図14-18 - <code>list</code>ビューのテーブルヘッダーはソートをコントロールする</p>

<p><img src="images/F1418.png" alt="listビューのテーブルヘッダーはソートはコントロールする" title="listビューのテーブルヘッダーはソートをコントロールする" /></p>

<p>あるひとつのカラムによってソートされたリストを直接指定する構文を再利用できます:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> link_to<span class="br0">&#40;</span><span class="st_h">'日付順のコメントリスト'</span><span class="sy0">,</span> <span class="st_h">'comment/list?sort=created_at&amp;type=desc'</span> <span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p><code>generator.yml</code>ファイルの<code>list</code>ビューに対して直接デフォルトの<code>sort</code>の順番を定義することも可能です。構文はリスト14-26で示された例に従います。</p>

<p>リスト14-26 - <code>list</code>ビューのなかで<code>sort</code>フィールドのデフォルトを設定する</p>

<pre><code>    list:
      sort:   created_at
      # ソートの順番を指定するための、代替構文
      sort:   [created_at, desc]
</code></pre>

<blockquote class="note"><p>
  実際のカラムに対応するフィールドだけが、ソートのコントロール機能に変換されます。カスタムもしくは部分テンプレートフィールドには対応していません。</p>
</blockquote>

<a name="customizing.the.pagination" id="customizing.the.pagination"></a><h4>パジネーションをカスタマイズする</h4>

<p>生成されたadministrationは大きなテーブルさえも効率的に処理します。<code>list</code>ビューがデフォルトでパジネーションを使うからです。テーブル内の実際の列の数がページごとの列の最大数を越えるとき、パジネーションのコントロール機能がリストの底に現れます。たとえば、図14-19はページごとに表示される5つのコメントの制限ではなく、テーブル内で6つのテストのコメントを持つコメントのリストを表示しますが、ページごとに表示されるコメント数は最大5まで制限されています。パジネーションはよいパフォーマンスとユーザービリティを保証します。データベースから表示される列のみが効率的に検索され、administrationモジュールによって何百万の列をかかえるテーブルでさえも管理できるからです。</p>

<p>図14-19 - パジネーションのコントロール機能は長いリスト上に現れる</p>

<p><img src="images/F1419.png" alt="パジネーションのコントロール機能は長いリスト上に現れる" title=" パジネーションのコントロール機能は長いリスト上に現れる" /></p>

<p><code>max_per_page</code>パラメーターによってそれぞれのページが表示されるレコードの数をカスタマイズできます:</p>

<pre><code>    list:
      max_per_page:   5
</code></pre>

<a name="using.a.join.to.speed.up.page.delivery" id="using.a.join.to.speed.up.page.delivery"></a><h4>ページ配信を加速するためにJoinを使う</h4>

<p>デフォルトでは、administrationジェネレーターはレコードのリストを検索する<code>doSelect()</code>を使います。しかし、リストで関連するオブジェクトを使う場合、リストを表示するために必要なデータベースクエリの数は急に増えることがあります。たとえば、コメントのリストで記事の名前を表示したい場合、関連する<code>Article</code>オブジェクトを検索するために追加クエリがリスト内のそれぞれの投稿に対して必要です。ですので、クエリの数を最適化するために、<code>doSelectJoinXXX()</code>メソッドを使うページャーを強制したい場合があるかもしれません。これは<code>peer_method</code>パラメーターで指定できます。</p>

<pre><code>    list:
      peer_method:   doSelectJoinArticle
</code></pre>

<p>18章ではJoinの概念についてより広範囲に説明します。</p>

<a name="edit.viewspecific.customization" id="edit.viewspecific.customization"></a><h3>editビュー固有のカスタマイズ</h3>

<p><code>edit</code>ビューにおいて、ユーザーは任意のレコードに対してそれぞれのカラムの値を修正できます。symfonyはカラムのデータ型にしたがって表示する入力のタイプを決定します。symfonyは<code>object_*_tag()</code>ヘルパーを生成し、そのヘルパーに編集するオブジェクトとプロパティを渡します。たとえば、ユーザーが<code>title</code>フィールドを編集できることを記事の<code>edit</code>ビューの設定が保証する場合です:</p>

<pre><code>    edit:
      display: [title, ...]
</code></pre>

<p>このカラムはスキーマで<code>varchar</code>型として定義されているので、<code>edit</code>ページはタイトルを編集するための通常のテキスト入力タグを表示します。</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> object_input_tag<span class="br0">&#40;</span><span class="re0">$article</span><span class="sy0">,</span> <span class="st_h">'getTitle'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<a name="changing.the.input.type" id="changing.the.input.type"></a><h4>入力タイプを変更する</h4>

<p>デフォルトの型からフィールドへの変換ルールはつぎのとおりです:</p>

<ul>
<li><code>integer</code>、<code>float</code>、<code>char</code>、<code>varchar(size)</code>として定義されたカラムは<code>edit</code>ビュー内で<code>object_input_tag()</code>として現れます。</li>
<li><code>longvarchar</code>として定義されたカラムは<code>object_textarea_tag()</code>として現れます。</li>
<li>外部キーのカラムは<code>object_select_tag()</code>として現れます。</li>
<li><code>boolean</code>として定義されたカラムは<code>object_checkbox_tag()</code>として現れます。</li>
<li><code>timestamp</code>もしくは<code>date</code>として定義されたカラムは<code>object_input_date_tag()</code>として現れます。</li>
</ul>

<p>任意のフィールドに対してカスタム入力タイプを指定するためにこれらのルールをオーバーライドしたい場合を考えます。その範囲で、<code>fields</code>定義内の<code>type</code>パラメーターを特定のフォームヘルパー名に設定します。生成された<code>object_*_tag()</code>オプションに関しては、<code>params</code>パラメーターで変更可能です。リスト14-27の例をご覧ください。</p>

<p>リスト14-27 - <code>edit</code>ビューに対してカスタム入力タイプとパラメーターを設定する</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    edit:
      fields:
                      ## 入力をドロップし、プレーンテキストを表示するだけ
        id:           { type: plain }
                      ## 入力は編集可能ではない
        author:       { params: disabled=true }
                      ## 入力はテキストエリアT(object_textarea_tag)
        content:      { type: textarea_tag, params: rich=true css=user.css tinymce_options=width:330 }
                      ## 入力は選択 (object_select_tag)
        article_id:   { params: include_custom=Choose an article }
         ...
</code></pre>

<p><code>param</code>パラメーターは生成された<code>object_*_tag()</code>にオプションとして渡されます。たとえば、先の<code>article_id</code>に対する<code>params</code>の定義はつぎのようにテンプレートのなかで生成されます:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> object_select_tag<span class="br0">&#40;</span><span class="re0">$comment</span><span class="sy0">,</span> <span class="st_h">'getArticleId'</span><span class="sy0">,</span> <span class="st_h">'related_class=Article'</span><span class="sy0">,</span> <span class="st_h">'include_custom=Choose an article'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>このことは通常フォームヘルパーで利用できるすべてのオプションは<code>edit</code>ビューでカスタマイズできることを意味します。</p>

<a name="handling.partial.fields" id="handling.partial.fields"></a><h4>部分テンプレートフィールドを扱う</h4>

<p>部分テンプレートフィールドは<code>list</code>ビューのように<code>edit</code>ビューで使用できます。違いは、アクションにおいて、部分テンプレートフィールドによって送られたリクエストパラメーターの値にしたがって、カラムの更新を手動で扱わなければならないことです。symfonyは(実際のカラムに対応する)通常のフィールドを処理する方法を理解していますが、部分テンプレートフィールド内にインクルードした入力を処理する方法を推測することはできません。</p>

<p>たとえば、利用可能なフィールドが<code>id</code>、<code>nickname</code>、と<code>password</code>である<code>User</code>クラスのためのadministrationモジュールを想像してください。サイトの管理者はユーザーの要望に応じてパスワードを変更できなければなりませんが、<code>edit</code>ビューはセキュリティの理由からパスワードフィールドの値を表示してはなりません。代わりに、値を変更するためにサイトの管理者が入力できる空のパスワード入力を表示します。このような<code>edit</code>ビューのためのジェネレーターの設定はリスト14-28と同じです。</p>

<p>リスト14-28 - 部分テンプレートフィールドを<code>edit</code>ビューに含める</p>

<pre><code>    edit:
      display:        [id, nickname, _newpassword]
      fields:
        newpassword:  { name: Password, help: Enter a password to change it, leave the field blank to keep the current one }
</code></pre>

<p><code>templates/_newpassword.php</code>部分テンプレートはつぎのようなコードを含みます:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> input_password_tag<span class="br0">&#40;</span><span class="st_h">'newpassword'</span><span class="sy0">,</span> <span class="st_h">''</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>この部分テンプレートは、オブジェクトフォームヘルパーではなく、シンプルなフォームヘルパーを使うことに注意してください。フォームの入力を投入するために現在の<code>User</code>オブジェクトからパスワードの値を検索するとユーザーのパスワードが公開されてしまう可能性があるので、望ましくないからです。</p>

<p>では、アクションのなかでオブジェクトを更新するためにこのコントロール機能から値を使うには、アクションのなかで<code>updateUserFromrequest()</code>メソッドを拡張する必要があります。これを行うには、リスト14-29のように、部分テンプレートフィールドの入力に対してカスタムふるまいを持つメソッドを、同じ名前でアクションのファイルのなかに作ります。</p>

<p>リスト14-29 - アクションの部分テンプレートフィールドを処理する(<code>modules/user/actions/actions.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> userActions <span class="kw2">extends</span> autouserActions
<span class="br0">&#123;</span>
  protected <span class="kw2">function</span> updateUserFromRequest<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// 部分テンプレートフィールドの入力を扱う</span>
    <span class="re0">$password</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getRequest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'newpassword'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$password</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">user</span><span class="sy0">-&gt;</span><span class="me1">setPassword</span><span class="br0">&#40;</span><span class="re0">$password</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="co1">// symfonyに別のフィールドを扱う</span>
    parent<span class="sy0">::</span><span class="me2">updateUserFromRequest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="note"><p>
  実際の世界において、通常の場合、間違った入力を避けるために<code>user/edit</code>ビューは2つの<code>password</code>フィールドを含みます。実際には、10章で見た通り、これはバリデーターを通して行われます。administraionによって生成されたモジュールは通常のモジュールと同じようにこのメカニズムから恩恵を受けます。</p>
</blockquote>

<a name="dealing.with.foreign.keys" id="dealing.with.foreign.keys"></a><h3>外部キーを扱う</h3>

<p>スキーマがテーブルのリレーションを定義する場合、生成されたadministrationモジュールはこれを活用して、より自動化されたコントロール方法を提供するので、リレーションの管理作業が大いに簡素化されます。</p>

<a name="onetomany.relationships" id="onetomany.relationships"></a><h4>一対多のリレーション</h4>

<p>1対多のテーブルのリレーションはadministrationジェネレーターによって考慮されます。以前の図14-1で記述されたように、<code>blog_comment</code>テーブルは<code>article_id</code>フィールドを通して<code>blog_article</code>テーブルとの関係を持ちます。<code>Comment</code>クラスのモジュールをadministrationジェネレーターによって初期化する場合、<code>comment/edit</code>アクションは<code>blog_article</code>テーブル内の利用可能なレコードのIDを示す<code>article_id</code>をドロップダウンリストとして表示します(説明図は図14-9を再度確認)。</p>

<p>加えて、<code>Article</code>オブジェクトで<code>__toString()</code>メソッドを定義する場合、ドロップダウンのオプションのテキストは主キーの代わりにこのメソッドを使います。</p>

<p><code>article</code>モジュール(多対一のリレーション)内部の記事に関連するコメントのリストを表示する場合、部分テンプレートフィールドの方法によってモジュールを少しカスタマイズする必要があります。</p>

<a name="manytomany.relationships" id="manytomany.relationships"></a><h4>多対多のリレーション</h4>

<p>symfonyはテーブルの多対多のリレーションも考慮しますが、これらをスキーマで定義できないので、いくつかのパラメーターを<code>generator.yml</code>ファイルに追加する必要があります。</p>

<p>多対多のリレーションの実装は中間のテーブルを必要とします。たとえば、<code>blog_article</code>と<code>blog_author</code>の間に多対多のリレーションがある場合(複数人によって記事が書かれ、当然のごとく著者は複数の記事を書くことができる)、図14-20に示すように、データベースはつねに<code>blog_article_author</code>などと呼ばれるテーブルに帰結します。</p>

<p>図14-20 - 多対多のリレーションを実装するためにthrough_classを利用する</p>

<p><img src="images/F1420.png" alt="多対多のリレーションを実装するためにthrough_classを利用する" title="多対多のリレーションを実装するために&quot;through class&quot;を利用する" /></p>

<p>モデルは<code>ArticleAuthor</code>と呼ばれるクラスを持ち、administrationジェネレーターが必要とする唯一のものです。しかし、これをフィールドの<code>through_class</code>パラメーターとして渡さなければなりません。</p>

<p>たとえば、<code>Article</code>クラスに基づいて生成されたモジュールにおいて、リスト14-30のように<code>generator.yml</code>を書く場合、<code>Author</code>クラスによる新しい多対多のリレーションを作るためにフィールドを追加できます。</p>

<p>リスト14-30 - <code>through_class</code>パラメーターで多対多のリレーションを扱う</p>

<pre><code>    edit:
      fields:
        article_author: { type: admin_double_list, params: through_class=ArticleAuthor }
</code></pre>

<p>フィールドは既存のオブジェクト間のリンクを処理するので、通常のドロップダウンリストは十分ではありません。そのためには特別な入力タイプを使わなければなりません。symfonyは2つのリストの関連するメンバーを助けする3つのウィジェットを提供します(図14-21に描かれています):</p>

<ul>
<li><code>admin_double_list</code>は2つの拡張された選択のコントロール機能のセットで、最初のリスト(利用可能な要素)から2番目のリスト(選択された要素)に要素を切り替えるボタンがあります。</li>
<li><code>admin_select_list</code>は多くの要素を選択できる拡張された選択のコントロール機能です。</li>
<li><code>admin_check_list</code>はチェックボックスタグのリストです。</li>
</ul>

<p>図14-21 - 多対多のリレーションに対して利用できるコントロール機能</p>

<p><img src="images/F1421.png" alt="多対多のリレーションに対して利用できるコントロール機能" title="多対多のリレーションに対して利用できるコントロール機能" /></p>

<a name="adding.interactions" id="adding.interactions"></a><h3>インタラクションを追加する</h3>

<p>administrationモジュールはユーザーが通常のCRUDオペレーションを実行できるようにしますが、ビューに対して独自のインタラクションを追加するもしくは可能なインタラクションを制限することもできます。たとえば、リスト14-31で示されているインタラクションを定義することで<code>article</code>モジュール上のデフォルトのすべてのCRUDアクションにアクセスできるようになります。</p>

<p>リスト14-31 - それぞれのビューに対してインタラクションを定義する(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre><code>    list:
      title:          List of Articles
      object_actions:
        _edit:         ~
        _delete:       ~
      batch_actions:
        _delete:       ~
      actions:
        _create:       ~

    edit:
      title:          Body of article %%title%%
      actions:
        _list:         ~
        _save:         ~
        _save_and_add: ~
        _delete:       ~
</code></pre>

<p><code>list</code>ビューにおいて、アクションの設定が3つ存在します: すべてのオブジェクトに対して利用可能なアクション(<code>object_actions</code>)、オブジェクトの選択に対して利用可能なアクション(<code>batch_actions</code>)、ページ全体に対して利用可能なアクション(<code>actions</code>)です。リスト14-31で定義されたリストのインタラクションは図14-22のようにレンダリングします。それぞれの行はレコードを編集するためのボタンとレコードを削除するためのボタン、それらに加えて、レコードの選択を削除するためにそれぞれの行の上に1つのチェックボックスを表示します。リストの一番下で、1つのボタンによって新しいレコードを作ることができます。</p>

<p>図14-22 - <code>list</code>ビュー内部のインタラクション</p>

<p><img src="images/F1422.png" alt="listビュー内部のインタラクション" title="listビュー内部のインタラクション" /></p>

<p><code>edit</code>ビューにおいて、一度に編集されるレコードは1つだけであり、(<code>actions</code>のもとで)定義するアクションのセットは1つだけです。リスト14-31で定義された<code>edit</code>インタラクションは図14-23のようにレンダリングされます。<code>save</code>アクションと<code>save_and_add</code>アクションは現在の編集をレコードに保存します。これらのアクションの違いは、<code>save</code>アクションは保存したあとで現在のレコード上に<code>edit</code>ビューを表示するのに対して、<code>save_adn_add</code>アクションは別のレコードを追加するために空の<code>empty</code>ビューを表示することです。<code>save_and_add</code>アクションは続けざまに多くのレコードを追加するときに非常に便利なショートカットです。<code>delete</code>アクションの位置に関しては、これはほかのボタンから分離されているので、ユーザーが誤ってクリックすることはありません。</p>

<p>アンダースコア(<code>_</code>)で始まるインタラクション名はsymfonyに対してこれらのアクションに対応するデフォルトのアイコンとアクションを使うように伝えます。administrationジェネレーターは<code>_edit</code>、<code>_delete</code>、<code>_list</code>、<code>_save</code>、<code>_save_and_add</code>、と<code>_create</code>を理解します。</p>

<p>図14-23 - <code>edit</code>ビュー内部のインタラクション</p>

<p><img src="images/F1423.png" alt="editビュー内部のインタラクション" title="editビュー内部のインタラクション" /></p>

<p>しかしカスタムインタラクションを追加することもできます。この場合リスト14-32で示されるように、アンダースコアで始まらない名前、と現在のモジュールのなかのターゲットのアクションを指定しなければなりません。</p>

<p>リスト14-32 - カスタムインタラクションを定義する</p>

<pre><code>    list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { name: Add a comment, action: addComment, icon: backend/addcomment.png }
</code></pre>

<p>図14-24で示されるように、リストにおけるそれぞれのアクションは<code>web/images/addcomment.png</code>を表示します。これをクリックすることで現在のモジュール内で<code>addComment</code>アクションを呼び出すことが行われます。現在のオブジェクトの主キーはリクエストパラメーターに自動的に追加されます。</p>

<p>図14-24 - <code>list</code>ビュー内部のカスタムインタラクション</p>

<p><img src="images/F1424.png" alt="listビュー内部ののカスタムインタラクション" title="listビュー内部のカスタムインタラクション" /></p>

<p><code>addComment</code>アクションはリスト14-33のように実装できます。</p>

<p>リスト14-33 - カスタムインタラクションアクション(<code>actions/actions.class.php</code>)</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> executeAddComment<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$comment</span> <span class="sy0">=</span> <span class="kw2">new</span> Comment<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="re0">$comment</span><span class="sy0">-&gt;</span><span class="me1">setArticleId</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="re0">$comment</span><span class="sy0">-&gt;</span><span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">redirect</span><span class="br0">&#40;</span><span class="st_h">'comment/edit?id='</span><span class="sy0">.</span><span class="re0">$comment</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>バッチスクリプトは<code>sf_admin_batch_selection</code>リクエストパラメーターのなかで選択されたレコードの主キーの配列を受けとります。</p>

<p>アクションについて最後の一言です: あるカテゴリのためにアクションを完全に抑制したい場合、リスト14-34で示されるように、空のリストを使います。</p>

<p>リスト14-34 - <code>list</code>ビューのすべてのアクションを除去する</p>

<pre><code>    list:
      title:          List of Articles
      actions:        {}
</code></pre>

<a name="form.validation" id="form.validation"></a><h3>フォームのバリデーション</h3>

<p>あなたのプロジェクトの<code>cache/</code>ディレクトリのなかで生成された<code>_edit_form.php</code>テンプレートを見る場合、<code>form</code>フィールドが特別な命名規約を使っていることがわかります。生成された<code>edit</code>ビューにおいて、入力名はアンダースコアの構文のモデルクラスの名前と角かっこで囲まれたフィールド名を連結することで作られます。</p>

<p>たとえば、<code>Article</code>クラスのための<code>edit</code>ビューが<code>title</code>フィールドを持つ場合、テンプレートはリスト14-35のようになりフィールドは<code>article[title]</code>として識別されます。</p>

<p>リスト14-35 - 生成された入力名の構文</p>

<pre><code>// generator.yml
generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default
    edit:
      display: [title]

// _edit_form.php テンプレートの結果
&lt;?php echo object_input_tag($article, 'getTitle', array('control_name' =&gt; 'article[title]')) ?&gt;

// 結果のHTML
&lt;input type="text" name="article[title]" id="article[title]" value="My Title" /&gt;
</code></pre>

<p>これは内部のフォームの扱い処理の間において多くの利点があります。しかしながら、10章で説明されたように、バリデーション設定が少し扱いにくくなるので、<code>fields</code>定義において、角かっこの<code>[]</code>を波かっこの<code>{}</code>に変更しなければなりません。バリデーターに対してフィールド名をパラメーターとして使うときもまた、生成されたHTMLのコードで現れるものとしてその名前を使うべきです(すなわち引用符で波かっこを囲む形式)。生成されたフォームのための特別なバリデーターの構文の詳細についてはリスト14-36を参照してください。</p>

<p>リスト14-36 - administrationで生成されたフォームのためのバリデーターファイルの構文</p>

<pre><code>## フィールドリストのなかの角かっこを波かっこで置き換える
fields:
  article{title}:
    required:
      msg: You must provide a title
    ## バリデーターパラメーターに対して、引用符で囲んだオリジナルのフィールド名を使う
    sfCompareValidator:
      check:        "user[newpassword]"
      compare_error: The password confirmation does not match the password.
</code></pre>

<a name="restricting.user.actions.using.credentials" id="restricting.user.actions.using.credentials"></a><h3>クレデンシャルを利用してユーザーのアクションを制限する</h3>

<p>特定のadministrationモジュールに対して、利用可能なフィールドとインタラクションはログインしたユーザーのクレデンシャルによって変化します(symfonyのセキュリティ機能の説明に関しては6章を参照)。</p>

<p>適切なクレデンシャルを持つユーザーのみに対して表示されるようにするためにジェネレーター内部のフィールドは<code>credentials</code>パラメーターを考慮に入れます。これは<code>list</code>ビューと<code>edit</code>ビューに対して機能します。加えて、ジェネレーターはクレデンシャルにしたがってインタラクションも隠すことができます。リスト14-37はこれらの機能のお手本を示しています。</p>

<p>リスト14-37 - クレデンシャルを使う(<code>generator.yml</code>)</p>

<pre><code>## idカラムはadminクレデンシャルを持つユーザーに対してのみ表示される
    list:
      title:          List of Articles
      layout:         tabular
      display:        [id, =title, content, nb_comments]
      fields:
        id:           { credentials: [admin] }

## addcommentインタラクションはadminクレデンシャルを持ったユーザーに制限される
    list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { credentials: [admin], name: Add a comment, action: addComment, icon: backend/addcomment.png }
</code></pre>

<a name="modifying.the.presentation.of.generated.modules" id="modifying.the.presentation.of.generated.modules"></a><h2>生成されたモジュールのプレゼンテーションを修正する</h2>

<p>独自のスタイルシートを適用するだけでなく、デフォルトのテンプレートでオーバーライドすることで、生成されたモジュールのプレゼンテーションが既存のグラフィカルな表にマッチするように、そのモジュールを修正できます。</p>

<a name="using.a.custom.style.sheet" id="using.a.custom.style.sheet"></a><h3>カスタムスタイルシートを使う</h3>

<p>生成されたHTMLコードは構造化された内容を持つので、プレゼンテーションであなたが望むことを大いに行うことができます。</p>

<p>リスト14-38で示されるように、<code>css</code>パラメーターを生成されたジェネレーターの設定に追加することで、administrationモジュールに対して、デフォルトの代わりにカスタムCSSを定義できます。</p>

<p>リスト14-38 - デフォルトの代わりにカスタムスタイルシートを使う</p>

<pre><code>generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default
    css:              mystylesheet
</code></pre>

<p>もう一つの方法として、ビュー単位でスタイルをオーバーライドするためにモジュールの<code>view.yml</code>によって提供されるメカニズムも利用できます。</p>

<a name="creating.a.custom.header.and.footer" id="creating.a.custom.header.and.footer"></a><h3>カスタムヘッダーとフッターを生成する</h3>

<p><code>list</code>ビューと<code>edit</code>ビューはヘッダーとフッター部分テンプレートを系統的に含むことができます。administrationモジュールの<code>templates/</code>ディレクトリのなかにはそのような部分テンプレートは存在しませんが、部分テンプレートを自動的にインクルードするには以下の名前の1つを追加する必要があります:</p>

<pre><code>_list_header.php
_list_footer.php
_edit_header.php
_edit_footer.php
</code></pre>

<p>たとえば、カスタムヘッダーを<code>article/edit</code>ビューに追加したい場合、リスト14-39のように<code>_edit_header.php</code>という名前のファイルを作ります。これは追加の設定なしで動作します。</p>

<p>リスト14-39 - <code>edit</code>ヘッダー部分テンプレートの例(<code>modules/articles/template/_edit_header.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getNbComments</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  &lt;h2&gt;この記事には<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getNbComments</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>のコメントがあります&lt;/h2&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">endif</span><span class="sy0">;</span> <span class="sy1">?&gt;</span></pre>

<p><code>edit</code>部分テンプレートはクラスによって命名された変数を通していつでも現在のオブジェクトにアクセスでき、<code>list</code>部分テンプレートは<code>$pager</code>変数を通していつでも現在のページャーにアクセスできることに留意してください。</p>

<blockquote class="sidebar"><p class="title">
  administrationアクションをカスタムパラメーターで呼び出す</p>
  
  <p>administrationモジュールアクションは<code>link_to()</code>ヘルパーの<code>query_string</code>引数を使用してカスタムパラメーターを受けとることができます。たとえば、記事に対するコメントへのリンクを持つ<code>previous_edit_header</code>部分テンプレートを拡張するには、つぎのように書きます:</p>
</blockquote>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getNbComments</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  &lt;h2&gt;この記事には<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> link_to<span class="br0">&#40;</span><span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getNbComments</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'のコメント'</span><span class="sy0">,</span> <span class="st_h">'comment/list'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'query_string'</span> <span class="sy0">=&gt;</span> <span class="st_h">'filter=filter&amp;filters%5Barticle_id%5D='</span><span class="sy0">.</span><span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>があります&lt;/h2&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">endif</span><span class="sy0">;</span> <span class="sy1">?&gt;</span></pre>

<blockquote class="sidebar"><p class="title"></p>
  
  <p>このクエリの文字列パラメーターはより読みやすいエンコードされたバージョンです。</p>

<pre class="php"><span class="st_h">'filter=filter&amp;filters[article_id]='</span><span class="sy0">.</span><span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span></pre>
  
  <p>これは<code>$article</code>に関連するコメントだけを表示するためにコメントをフィルタリングします。<code>query_string</code>引数を使うことで、ソートの順番かつ/またはカスタムlistビューを表示するフィルターを指定することもできます。これはカスタムインタラクションに対しても便利です。</p>
</blockquote>

<a name="customizing.the.theme" id="customizing.the.theme"></a><h3>テーマをカスタマイズする</h3>

<p>カスタム要件を満たすために、モジュールの<code>templates/</code>フォルダーのなかでオーバーライドできるフレームワークから継承された別の部分テンプレートが存在します。</p>

<p>ジェネレーターのテンプレートは個別にオーバーライドできる小さな部分に分割可能で、アクションは一つずつ変更できます。</p>

<p>しかしながら、同じ方法でいくつかのモジュールに対してこれらをオーバーライドしたいのであれば、おそらくは再利用可能なテーマを作るべきです。テーマ(theme)はテンプレートとアクションの完全なセットで、<code>generator.yml</code>の始めでテーマの値に指定された場合、administrationモジュールのなかで使用できます。デフォルトのテーマと一緒に、symfonyは<code>$sf_symfony_lib_dir/plugins/sfPropelPlugin/data/generator/sfPropelAdmin/default/</code>で定義されたファイルを使います。</p>

<p>テーマのファイルは<code>$sf_symfony_lib_dir/plugins/sfPropelPlugin/data/generator/sfPropelAdmin/default/template/</code>ディレクトリ内部の、プロジェクトのツリー構造に設置しなければならず、デフォルトのテーマからファイルをコピーすることで新しいテーマを使い始めることができます(<code>$sf_symfony_lib_dir/plugins/sfPropelPlugin/data/generator/sfPropelAdmin/default/template/</code>ディレクトリに設置されます)。こうすることで、カスタムテーマテーマを用いるために必要なすべてのファイルがきちんと存在することになります:</p>

<pre><code>// [theme_name]/template/templates/内の、部分テンプレート
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php

// [theme_name]/template/actions/actions.class.php内のアクション
processFilters()     // リクエストフィルターを処理する
addFiltersCriteria() // フィルターをCriteriaオブジェクトに追加する
processSort()
addSortCriteria()
</code></pre>

<p>テンプレートファイルは実際にはテンプレートのテンプレート(templates of templates)であることに注意してください。すなわち、PHPのファイルはジェネレーターの設定に基づいたテンプレートを生成する特別なユーティリティによって解析されます(これはコンパイレーションフェーズ(compilation phase)と呼ばれます)。生成されたテンプレートが実際にブラウジングしている間に実行されるPHPのコードを含まなければならないので、テンプレートのテンプレートは最初のパスの間にPHPのコードを実行しないようにするために代替構文を使います。リスト14-40はデフォルトのテンプレートのテンプレートの抜粋です。</p>

<p>リスト14-40 - テンプレートのテンプレートの構文</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getPrimaryKey</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="kw1">as</span> <span class="re0">$pk</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
[?php echo object_input_hidden_tag($<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getSingularName</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>,'get<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$pk</span><span class="sy0">-&gt;</span><span class="me1">getPhpName</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>') ?]
<span class="kw2">&lt;?php</span> <span class="kw1">endforeach</span><span class="sy0">;</span> <span class="sy1">?&gt;</span></pre>

<p>このリストにおいて、(コンパイル時に)<code>&lt;?</code>によって導入されたPHPのコードは即座に実行され、<code>[?</code>によって導入されたものは実行時のみに実行されますが、テンプレートエンジンは最後には<code>[?</code>タグを<code>&lt;?</code>タグに変換するのでテンプレートの結果はつぎのようになります:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> object_input_hidden_tag<span class="br0">&#40;</span><span class="re0">$article</span><span class="sy0">,</span> <span class="st_h">'getId'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>テンプレートのテンプレートは扱いにくいので、独自のテーマを作りたい場合、もっともお勧めすることはデフォルトのテーマから始め、少しずつ修正し、頻繁にテストすることです。</p>

<blockquote class="tip"><p>
  ジェネレーターのテーマをプラグインのパッケージにすることができるので、再利用性が高くなり、複数のアプリケーションにまたがってデプロイすることが簡単になります。詳細な情報は17章を参照してください。</p>
</blockquote>

<p>-</p>

<blockquote class="sidebar"><p class="title">
  独自のジェネレーターを開発する</p>
  
  <p>administrationジェネレーターはsymfonyの内部コンポーネントのセットを使います。内部コンポーネントはキャッシュ内部に生成されたアクションとテンプレート、テーマの使用、テンプレートのテンプレートの解析を自動化します。</p>
  
  <p>このことはsymfonyが既存のジェネレーターとは似ているもしくは完全に異なる独自のジェネレーターを作るためのすべてのツールを提供することを意味します。モジュールの生成は<code>sfGeneratorManager</code>クラスの<code>generate()</code>メソッドで管理されます。たとえば、administrationを生成するために、symfonyは内部でつぎのコードを呼び出します:</p>

<pre class="php"><span class="re0">$generator_manager</span> <span class="sy0">=</span> <span class="kw2">new</span> sfGeneratorManager<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$data</span> <span class="sy0">=</span> <span class="re0">$generator_manager</span><span class="sy0">-&gt;</span><span class="me1">generate</span><span class="br0">&#40;</span><span class="st_h">'sfPropelAdminGenerator'</span><span class="sy0">,</span> <span class="re0">$parameters</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>
  
  <p>独自のジェネレーターを開発したい場合、<code>sfGeneratorManeger</code>クラスと<code>sfGenerator</code>クラスのAPIドキュメントを見て、<code>sfAdminGenerator</code>クラスと<code>sfCRUDGenerator</code>クラスを例としてください。</p>
</blockquote>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>バックエンドのアプリケーションを自動的に生成したい場合、基本はよく定義されたスキーマとオブジェクトモデルです。administrationのカスタマイズによって生成されたモジュールのカスタマイゼーションの大半は設定を通して行われます。</p>

<p><code>generator.yml</code>ファイルは生成されたバックエンドのプログラミングにおいて中心的な役割を果たします。このファイルによって内容、機能、<code>list</code>ビューと<code>edit</code>ビューの見た目を完全にカスタマイズできます。またPHPのコードを一行も書かずに、フィールドラベル、ツールチップ、フィルター、ソートの順番、ページのサイズ、入力タイプ、外部のリレーション、カスタムインタラクション、とクレデンシャルをYAMLで直接管理できます。</p>

<p>administrationジェネレーターが必要な機能をネイティブにサポートしない場合、アクションをオーバーライドする部分テンプレートフィールドと機能は完全な拡張性を提供します。それに加えて、テーマのメカニズムのおかげで、administrationジェネレーターのメカニズムへの適合方法を再利用できます。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="13-I18n-and-L10n.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="15-Unit-and-Functional-Testing.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
